<template>
  <div class="upload" :class="{ 'is-disabled': disabled }" ref="uploadEl" @click="doUpload" v-loading="loading">
    <input type="file" ref="file" :accept="accept" :multiple="multiple" :disabled="disabled" class="file-input"
      @change="fileChange" />
    <div class="upload-card" :class="{ active: isActive }" v-if="card">
      <slot>
        <el-icons class="icon" name="uploadFilled"></el-icons>
        <div>{{ placeholder }}</div>
      </slot>
    </div>
    <slot v-else></slot>
  </div>
</template>

<script setup>
import { uploadFile } from '@/api';
import { ElMessage } from 'element-plus';
import { ref, toRefs, onMounted } from 'vue';

const emit = defineEmits(['change', 'upload']);
const props = defineProps({
  autoUpload: Boolean,
  accept: {
    type: String,
    default: ''
  },
  multiple: Boolean,
  disabled: Boolean,
  limitSize: {
    type: Number,
    default: 50
  }, // 单文件最大值（单位M）
  limitNumber: {
    type: Number,
    default: 1
  }, // 文件上传限制数量
  card: Boolean,
  placeholder: {
    type: String,
    default: '请点击/拖拽文件上传'
  },
  ossPath: {
    type: String,
    default: 'upload_files'
  },
})
const { autoUpload, accept, multiple, disabled, limitSize, limitNumber, card } =
  toRefs(props);

const file = ref();
const loading = ref(false);
function doUpload() {
  if (loading.value) {
    return false;
  }
  file.value.click();
}
defineExpose({
  doUpload
})

function fileChange(e) {
  const target = e.target
  if (target.files) {
    todoUpload(Array.from(target.files));
  }
  target.value = '';
}

function todoUpload(result) {
  const { passed, files } = validateFile(result);
  if (!passed) {
    const msgs = ['文件过大'];
    if (accept.value) {
      msgs.push('文件类型有误');
    }
    if (limitNumber.value) {
      msgs.push('文件数量超限');
    }
    ElMessage.warning(msgs.join('或者'));
    result = files;
  }
  if (!result.length) {
    return false;
  }
  if (autoUpload.value) {
    onUpload(result)
  } else {
    emit('change', result);
  }
}

function onUpload(files) {
  loading.value = true;
  Promise.all(
    files.map(file => uploadFile({ file, oss_path: props.ossPath }))
  )
    .then(resList => {
      const pathList = resList.map(v => {
        return v.data.url
      });
      emit('upload', pathList, files);
    })
    .finally(() => {
      loading.value = false;
    });
}

function validateFile(files) {
  const len = files.length;
  files = files.filter((file) => {
    const { type, name, size } = file;
    const checkMaxSize = size < limitSize.value * 1024 * 1024;
    if (!checkMaxSize) return false;

    if (accept.value) {
      const extension = name.includes('.') ? `.${name.split('.').pop()}` : '';
      const baseType = type.replace(/\/.*$/, '');
      const checkAccept = accept.value
        .split(',')
        .map((type2) => type2.trim())
        .filter((type2) => type2)
        .some((acceptedType) => {
          if (acceptedType.startsWith('.')) {
            return extension === acceptedType;
          }
          if (/\/\*$/.test(acceptedType)) {
            return baseType === acceptedType.replace(/\/\*$/, '');
          }
          if (/^[^/]+\/[^/]+$/.test(acceptedType)) {
            return type === acceptedType;
          }
          return false;
        });
      return checkAccept;
    }
    return true;
  });
  if (limitNumber.value && files.length > limitNumber.value) {
    files.length = limitNumber.value;
  }
  return { files, passed: files.length === len };
}

const uploadEl = ref();
const isActive = ref(false);
onMounted(function () {
  uploadEl.value.addEventListener('drop', onDrop, false);
  uploadEl.value.addEventListener('dragover', onDragover, false);
  uploadEl.value.addEventListener('dragleave', onDragleave, false);
});
function onDrop(e) {
  e.preventDefault();
  if (e.dataTransfer) {
    todoUpload(Array.from(e.dataTransfer.files));
  }
  onDragleave();
}
function onDragover(e) {
  e.preventDefault();
  isActive.value = true;
}
function onDragleave() {
  isActive.value = false;
}
</script>

<style lang="scss" scoped>
.upload {
  position: relative;
  display: inline-block;

  &.is-disabled {
    cursor: not-allowed;

    .upload-card {
      cursor: not-allowed;
    }
  }

  .file-input {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    z-index: -1;
  }

  .upload-card {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    width: 240px;
    height: 160px;
    border: 1px dashed #e9e9e9;
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    font-size: 14px;
    transition: border-color 0.3s;

    &:hover,
    &.active {
      border-color: var(--el-color-primary);
    }

    .icon {
      font-size: 32px;
      margin-bottom: 8px;
    }
  }
}

:deep {
  .el-loading-text {
    position: absolute;
    left: 100%;
    top: 0;
    min-width: 70px;
    background: rgba(255, 255, 255, 0.9);
    color: var(--el-color-warning);
  }
}
</style>
